/*
 * File:   ClientProtocol.cpp
 * Author: wilq
 *
 * Created on June 10, 2012, 12:30 PM
 */

#include "ClientProtocol.h"
#include "Exceptions.h"


string ClientProtocol::_Get_String(const NetworkPacket &packet, uint32_t position) {
    char buffor[256];
    buffor[0] = 0;
    packet.Get_String(position, buffor, 255);
    buffor[255] = 0;
    return string(buffor);
}

File ClientProtocol::_Get_File(NetworkPacket &packet, uint32_t &position) {
    File file;
    packet.Get_U32(position++, file.id);
    packet.Get_U32(position++, file.parentId);
    packet.Get_U32(position++, file.ownerGroupId);
    packet.Get_U32(position++, file.ownerUserId);
    packet.Get_U32(position++, file.storageId);

    packet.Get_U32(position++, file.rights);
    packet.Get_U32(position++, file.size);

    packet.Get_U64(position++, file.createDate);
    packet.Get_U64(position++, file.lastAccesDate);
    packet.Get_U64(position++, file.lastModificationDate);

    file.link = _Get_String(packet, position++);
    file.name = _Get_String(packet, position++);

    return file;
}

Folder ClientProtocol::_Get_Folder(NetworkPacket &packet, uint32_t &position) {
    Folder folder;
    packet.Get_U32(position++, folder.id);
    packet.Get_U32(position++, folder.parentId);
    packet.Get_U32(position++, folder.ownerGroupId);
    packet.Get_U32(position++, folder.ownerUserId);
    packet.Get_U32(position++, folder.storageId);

    packet.Get_U32(position++, folder.rights);

    packet.Get_U64(position++, folder.createDate);

    folder.link = _Get_String(packet, position++);
    folder.name = _Get_String(packet, position++);
    return folder;
}

User ClientProtocol::_Get_User(NetworkPacket &packet, uint32_t &position) {
    User user;
    packet.Get_U32(position++, user.id);
    user.name = _Get_String(packet, position++);
    packet.Get_U32(position++, user.createDate);
    packet.Get_U32(position++, user.lastAccessDate);
    packet.Get_U32(position++, user.rights);
    return user;
}

Storage ClientProtocol::_Get_Storage(NetworkPacket &packet, uint32_t &position) {
    Storage storage;
    packet.Get_U32(position++, storage.id);
    storage.name = _Get_String(packet, position++);
    packet.Get_U32(position++, storage.createDate);
    packet.Get_U32(position++, storage.lastAccessDate);
    return storage;
}

Group ClientProtocol::_Get_Group(NetworkPacket &packet, uint32_t &position) {
    Group group;
    packet.Get_U32(position++, group.id);
    group.name = _Get_String(packet, position++);
    packet.Get_U32(position++, group.createDate);
    packet.Get_U32(position++, group.baseRights);
    return group;
}


ClientProtocol::ClientProtocol(socket_ptr s) : CSNetworkLib::NetworkProtocol(s) {
    FUNCTION_TRACK
}

ClientProtocol::~ClientProtocol() {
    FUNCTION_TRACK
}

int32_t ClientProtocol::UserAuthorization(const string &name, const string &password) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId(CSNetworkLib::PACKET_CLIENT_AutorizationReq);

    request.Push_String(name.c_str());
    request.Push_String(password.c_str());

    int32_t userId = 0;
    if (SendPacketWait(request, response, 5000)) {
        response.Get_S32(0, userId);
    }
    return userId;
}

vector<File> ClientProtocol::GetFilesInDir(uint32_t folderId) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserGetFilesInDir);

    request.Push_S32(folderId);

    uint32_t count = 0;
    vector<File> list;

    if (SendPacketWait(request, response)) {

        uint32_t position = 0;

        response.Get_U32(position++, count);

        for (int32_t i = 0; i < count; i++) {
            list.push_back(_Get_File(response, position));
        }

    }

    return list;
}

vector<Folder> ClientProtocol::GetFoldersInDir(uint32_t folderId) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserGetFoldersInDir);

    request.Push_S32(folderId);

    uint32_t count = 0;
    vector<Folder> list;

    if (SendPacketWait(request, response)) {

        uint32_t position = 0;

        response.Get_U32(position++, count);

        DBUG("getFoldersInDir %d ", count);

        for (int32_t i = 0; i < count; i++) {
            list.push_back(_Get_Folder(response, position));
        }

    }

    return list;
}

bool ClientProtocol::FileDelete(const std::string &path) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_CLIENT_FileDeleteReq);

    request.Push_String(path.c_str());

    if (SendPacketWait(request, response)) {
        bool status = false;
        response.Get_Bool(0, status);
        return status;
    } else {
        return false;
    }
}

int32_t ClientProtocol::RenameFile(uint32_t fileId, string name) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserRenameFile);

    request.Push_U32(fileId);
    request.Push_String(name.c_str());

    int32_t status = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, status);
        return status;
    } else {
        return -1;
    }
}

int32_t ClientProtocol::MakeDirectory(uint32_t folderId, string name) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserCreateFolder);

    request.Push_S32(folderId);
    request.Push_String(name.c_str());

    int32_t status = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, status);
        return status;
    } else {
        return -1;
    }
}

int32_t ClientProtocol::DeleteDirectory(uint32_t folderId) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserDeleteFolder);

    request.Push_U32(folderId);
    int32_t status = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, status);
        return status;
    } else {
        return -1;
    }
}

int32_t ClientProtocol::RenameDirectory(uint32_t folderId, string name) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserRenameFolder);

    request.Push_U32(folderId);
    request.Push_String(name.c_str());

    int32_t status = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, status);
        return status;
    } else {
        return -1;
    }
}

int32_t ClientProtocol::AddUser(string name, string password) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserAddUser);

    request.Push_String(name.c_str());
    request.Push_String(password.c_str());

    int32_t id = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, id);
        return id;
    } else {
        return -1;
    }
}

int32_t ClientProtocol::ChangeUserPassword(string name, string password) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserChangePassword);

    request.Push_String(name.c_str());
    request.Push_String(password.c_str());

    int32_t id = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, id);
        return id;
    } else {
        return -1;
    }
}

int32_t ClientProtocol::RemoveUser(uint32_t userId) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserRemoveUser);
    request.Push_U32(userId);

    int32_t id = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, id);
        return id;
    } else {
        return -1;
    }
}

vector<User> ClientProtocol::GetUsersList() {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserRemoveUser);

    uint32_t count = 0;
    vector<User> list;

    if (SendPacketWait(request, response)) {

        uint32_t position = 0;

        response.Get_U32(position++, count);

        for (int32_t i = 0; i < count; i++) {
            list.push_back(_Get_User(response, position));
        }
    }

    return list;
}

int32_t ClientProtocol::AddGroup(string name) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserAddGroup);

    request.Push_String(name.c_str());

    int32_t id = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, id);
        return id;
    } else {
        return -1;
    }
}

int32_t ClientProtocol::RemoveGroup(uint32_t groupId) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserRemoveGroup);
    request.Push_U32(groupId);

    int32_t id = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, id);
        return id;
    } else {
        return -1;
    }
}

vector<Group> ClientProtocol::GetGroupList() {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserRemoveUser);

    uint32_t count = 0;
    vector<Group> list;

    if (SendPacketWait(request, response)) {

        uint32_t position = 0;

        response.Get_U32(position++, count);

        for (int32_t i = 0; i < count; i++) {
            list.push_back(_Get_Group(response, position));
        }
    }

    return list;
}

int32_t ClientProtocol::AddStorage(string name, string key) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserAddStorage);

    request.Push_String(name.c_str());
    request.Push_String(key.c_str());

    int32_t id = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, id);
        DBUG("Add storage status %d", id);
        return id;
    } else {
        DBUG("No response !");
        return -1;
    }
}

int32_t ClientProtocol::RemoveStorage(uint32_t storageId) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserRemoveStorage);
    request.Push_U32(storageId);

    int32_t id = 0;

    if (SendPacketWait(request, response)) {
        response.Get_S32(0, id);
        return id;
    } else {
        return -1;
    }
}

int32_t ClientProtocol::ChangeStorageKey(string storage, string key) {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserChangeStorageKey);

    request.Push_String(storage.c_str());
    request.Push_String(key.c_str());

    if (SendPacketWait(request, response)) {
        int32_t id = 0;
        response.Get_S32(0, id);
        return id!=0?0:1;
    } else {
        return -1;
    }
}

vector<Storage> ClientProtocol::GetStorageList() {
    FUNCTION_TRACK
    CSNetworkLib::NetworkPacket request;
    CSNetworkLib::NetworkPacket response;

    request.SetId((int32_t) CSNetworkLib::PACKET_UserRemoveUser);

    uint32_t count = 0;
    vector<Storage> list;

    if (SendPacketWait(request, response)) {

        uint32_t position = 0;

        response.Get_U32(position++, count);

        for (int32_t i = 0; i < count; i++) {
            list.push_back(_Get_Storage(response, position));
        }
    }

    return list;
}

bool ClientProtocol::Share(const std::string &virtualPath, const std::string &storagePath, const std::string &storageName, bool force) {
    FUNCTION_TRACK

    if (virtualPath.empty() || storagePath.empty() || storageName.empty()) {
        throw CSException_WrongArgument("All method parameters are empty");
    }

    CSNetworkLib::NetworkPacket request(CSNetworkLib::PACKET_CLIENT_ShareReq);
    CSNetworkLib::NetworkPacket response;

    request.Push_String(virtualPath.c_str());
    request.Push_String(storagePath.c_str());
    request.Push_String(storageName.c_str());
    request.Push_Bool(force);
    if (SendPacketWait(request, response)) {
        bool resp = false;
        response.Get_Bool(0, resp);
        return resp;
    }
    return false;
}

bool ClientProtocol::UnShare(const std::string &virtualPath, bool remove) {
    FUNCTION_TRACK

    if (virtualPath.empty()) {
        throw CSException_WrongArgument("All method parameters are empty");
    }

    CSNetworkLib::NetworkPacket request(CSNetworkLib::PACKET_CLIENT_UnShareReq);
    CSNetworkLib::NetworkPacket response;

    request.Push_String(virtualPath.c_str());
    request.Push_Bool(remove);
    if (SendPacketWait(request, response)) {
        bool resp = false;
        response.Get_Bool(0, resp);
        return resp;
    }
    return false;
}

bool ClientProtocol::FileExists(const std::string &path)
{
    FUNCTION_TRACK

    if (path.empty()) {
        throw CSException_WrongArgument("All method parameters are empty");
    }

    CSNetworkLib::NetworkPacket request(CSNetworkLib::PACKET_CLIENT_FileExistsReq);
    CSNetworkLib::NetworkPacket response;

    request.Push_String(path.c_str());
    if (SendPacketWait(request, response)) {
        bool resp = false;
        response.Get_Bool(0, resp);
        return resp;
    }
    return false;
}


THANDLE ClientProtocol::FileOpen(const std::string &path, const std::string &mode) {
    FUNCTION_TRACK
    if (path.empty() || mode.empty()) {
        throw CSException_WrongArgument("Method parameters are empty");
    }
    CSNetworkLib::NetworkPacket request(CSNetworkLib::PACKET_CLIENT_FileOpenReq);
    CSNetworkLib::NetworkPacket response;

    request.Push_String(path.c_str());
    request.Push_String(mode.c_str());

    if (SendPacketWait(request, response)) {
        THANDLE status = 0;
        response.Get_U32(0, status);
        return status;
    } else {
        return 0;
    }
}

bool ClientProtocol::FileClose(THANDLE handle) {
    FUNCTION_TRACK
    if (0 == handle) {
        throw CSException_WrongArgument("Method parameters are empty");
    }
    CSNetworkLib::NetworkPacket request(CSNetworkLib::PACKET_CLIENT_FileCloseReq);
    CSNetworkLib::NetworkPacket response;
    request.Push_U32(handle);

    if (SendPacketWait(request, response)) {
        bool status = false;
        response.Get_Bool(0, status);
        return status;
    } else {
        return false;
    }
}

 uint64_t ClientProtocol::FileInfo(THANDLE handle, uint8_t mode)
 {
    FUNCTION_TRACK
    if (0 == handle) {
        throw CSException_WrongArgument("Method parameters are empty");
    }
    CSNetworkLib::NetworkPacket request(CSNetworkLib::PACKET_CLIENT_FileInfoReq);
    CSNetworkLib::NetworkPacket response;

    request.Push_U32(handle);
    request.Push_U8(mode);

    if (SendPacketWait(request, response)) {
        uint64_t r = 0;
        response.Get_U64(0, r);
        return r;
    } else {
        return 0;
    }
 }

bool ClientProtocol::FileEof(THANDLE handle)
{
    FUNCTION_TRACK
    if (0 == handle) {
        throw CSException_WrongArgument("Method parameters are empty");
    }
    CSNetworkLib::NetworkPacket request(CSNetworkLib::PACKET_CLIENT_FileEofReq);
    CSNetworkLib::NetworkPacket response;

    request.Push_U32(handle);
    bool r = true;
    if (SendPacketWait(request, response)) {
        response.Get_Bool(0, r);
    }
    return r;
}

bool ClientProtocol::FileSeek(THANDLE handle, int64_t position /*-1 for end*/)
{
    FUNCTION_TRACK
    if (0 == handle) {
        throw CSException_WrongArgument("Method parameters are empty");
    }
    CSNetworkLib::NetworkPacket request(CSNetworkLib::PACKET_CLIENT_FileSeekReq);
    CSNetworkLib::NetworkPacket response;

    request.Push_U32(handle);
    request.Push_S64(position);

    if (SendPacketWait(request, response)) {
        bool r = false;
        response.Get_Bool(0, r);
        return r;
    } else {
        return false;
    }
}

uint32_t ClientProtocol::FileWrite(THANDLE handle, uint32_t size, const uint8_t *data)
{
    if (0 == handle || NULL == data) {
        throw CSException_WrongArgument("Method parameters are empty");
    }
    if (size == 0)
        return 0;

    uint32_t s = 0;

    while(s<size)
    {
        CSNetworkLib::NetworkPacket request(CSNetworkLib::PACKET_CLIENT_FileWriteReq);
        CSNetworkLib::NetworkPacket response;
        request.Push_U32(handle);
        request.Push_Data(&data[s], ((size-s)>(BUFFOR_LENGTH-1024))?(BUFFOR_LENGTH-1024):(size-s));
        if (!SendPacketWait(request, response))
        {
            return s;
        }
        uint32_t ret = 0;
        response.Get_U32(0, ret);
        if ( 0 == ret)
            return s;
        s += ret;
    }
    return s;
}

uint32_t ClientProtocol::FileRead(THANDLE handle, uint32_t size, uint8_t *data)
{
    if (0 == handle || NULL == data) {
        throw CSException_WrongArgument("Method parameters are empty");
    }
    if (size == 0)
        return 0;

    uint32_t s = 0;

    while(s<size)
    {
        CSNetworkLib::NetworkPacket request(CSNetworkLib::PACKET_CLIENT_FileReadReq);
        CSNetworkLib::NetworkPacket response;
        request.Push_U32(handle);
        request.Push_U32(((size-s)>(BUFFOR_LENGTH-1024))?(BUFFOR_LENGTH-1024):(size-s));
        if (!SendPacketWait(request, response))
        {
            return s;
        }
        if (!response.Get_Data(0, &data[s], size-s))
        {
            return s;
        }
        if ((((size-s)>(BUFFOR_LENGTH-1024))?(BUFFOR_LENGTH-1024):(size-s)) > response.GetItemSize(0))
        {
            return s+response.GetItemSize(0);
        }
        s += response.GetItemSize(0);
    }
    return s;
}